#include"renderarea.h"

int Utils::findPointIndex(const QVector<QPoint>& points, const QPoint& target) {
    for (size_t i = 0; i < points.size(); ++i) {
        if (points[i] == target) {
            return static_cast<int>(i); // 返回找到的索引
        }
    }
    return -1; // 如果没有找到，返回 -1
}
void Utils::addPoint(RenderArea* renderArea,int x,int y){
    if(renderArea->point_list.size()>=renderArea->maxPointNum)return;
    QPoint p(x,y);
    renderArea->point_list.push_back(p);
    // 添加每个点头顶的位置信息
    QLabel* label = new QLabel(renderArea);
    QString position_text = QString("(%1,%2)").arg(x).arg(y);
    label->setText(position_text);
    label->setVisible(true);
    label->move(x-25,y-20);
    renderArea->pointLabelMap.insert(p,label);
    // 给dj_graph加一行
    renderArea->dj_graph.push_back({});

}
bool Utils::isPointOnLine(const QLineF &line, const QPointF &point, double tolerance) {
    double len = line.length(); // 线段的长度
    if (qFuzzyCompare(len, 0.0)) return false; // 如果线段长度为0，则直接返回false

    // 计算点到线段起点和终点的距离
    double distStartToPoint = QLineF(line.p1(), point).length();
    double distEndToPoint = QLineF(line.p2(), point).length();

    // 检查点到两顶点的距离之和是否与线段长度相等（考虑容差）
    double t = qAbs((distStartToPoint + distEndToPoint) - len);
    qDebug()<<t;
    return t <= tolerance;
}

void Utils::buildPointMenu(RenderArea* renderArea,const QPoint& pos,int point_index){
    QMenu contextMenu(renderArea);
    QAction action1("删除点及相关的线", renderArea);
    QPoint real_point = renderArea->point_list[point_index];
    QAction::connect(&action1, &QAction::triggered, renderArea, [&real_point,point_index,renderArea,&pos](){
        qDebug()<<"删除点及相关的线";
        qDebug()<<"real_point:"<<real_point;
        auto it = std::remove_if(renderArea->line_list.begin(), renderArea->line_list.end(),[renderArea,&real_point](const QLine& line){
                      return (line.p1()==real_point||line.p2()==real_point);
        });
        if(it!=renderArea->line_list.end()){
            // 这里的it代表的是remove_if找到的符合条件的最左边的那个迭代器所以这里应该是一个循环的操作
            for(auto line_iterator = it;line_iterator<renderArea->line_list.end();line_iterator++){
                qDebug()<<"现在是找到了要删除的边:"<<*line_iterator;
                QLine line = *line_iterator;
                int p1_index = Utils::findPointIndex(renderArea->point_list,line.p1());
                int p2_index =  Utils::findPointIndex(renderArea->point_list,line.p2());
                renderArea->setUnDirectionGraphStateBetweenTwoPoint(p1_index,p2_index,0);
                qDebug()<<"现在点:"<<renderArea->point_list[p1_index]<<"和点:"<<renderArea->point_list[p2_index]<<"之间的点状态已经为:"<<renderArea->undirected_graph[p1_index][p2_index];
            }
            renderArea->line_list.erase(it,renderArea->line_list.end());
            qDebug()<<"现在的line_list为："<<renderArea->line_list;
            // 删除这里的bug现象：当创建三个点并连接三条线后删除点和线之后再次想重新连接时会失败一条线
            // 原因：因为删除边以后还会删除点导致点的index和c语言风格的无向图的i,j对不上了，出现问题
            // 解决方案：建议直接重构,否则感觉之后这种类似的问题会越来越多
        }
        // 最后删除pos点
        if(renderArea->pointLabelMap.contains(real_point)){
            QLabel* label = renderArea->pointLabelMap.value(real_point);
            if (label) {
                label->deleteLater();
            }
        }
        renderArea->point_list.erase(renderArea->point_list.begin()+point_index);
        // 删除dj_graph里面对应的行和其它行中to等于这个点的Edge元素
        renderArea->dj_graph.erase(renderArea->dj_graph.begin()+point_index);
        for(auto& row:renderArea->dj_graph){
            row.erase(std::remove_if(row.begin(),row.end(),[point_index](const Edge& e){
                          return e.to == point_index;
                      }),row.end());
        }
    });
    contextMenu.addAction(&action1);
    QAction action2("设置为起点", renderArea);
    QAction::connect(&action2, &QAction::triggered, renderArea, [renderArea,&real_point](){
        qDebug() << "设置为起点";
        renderArea->departure = real_point;
        qDebug()<<"departure now is :"<<renderArea->departure;
    });
    contextMenu.addAction(&action2);
    QAction action3("设置为终点", renderArea);
    QAction::connect(&action3, &QAction::triggered, renderArea, [renderArea,&real_point](){
        qDebug() << "设置为终点";
        renderArea->destination = real_point;
        qDebug()<<"destination now is :"<<renderArea->destination;
    });
    contextMenu.addAction(&action3);
    QPoint globalPos = renderArea->mapToGlobal(pos);
    contextMenu.exec(globalPos);
}
void Utils::buildLineMenu(RenderArea* renderArea,const QPoint& pos,int line_index){
    QMenu contextMenu(renderArea);
    QAction action1("删除当前线", renderArea);
    QAction::connect(&action1, &QAction::triggered, renderArea, [line_index,renderArea](){
        // 获取这条线上的两个点
        // 偷懒了这里不提出去了,如果要提的话应该是一个private的函数是比较合理的
        QLine this_line = renderArea->line_list[line_index];
        QPoint p1 = this_line.p1();
        QPoint p2 = this_line.p2();
        int p1_index = findPointIndex(renderArea->point_list,p1);
        int p2_index = findPointIndex(renderArea->point_list,p2);
        qDebug()<<"p1_index:"<<p1_index<<"p2_index:"<<p2_index;
        renderArea->setUnDirectionGraphStateBetweenTwoPoint(p1_index,p2_index,0);
        // 删除dj_graph里面的点

        // 这里的删除逻辑有问题
        // fix:因为我这里用auto p1_row这是相当于复制了一个副本等于dj_graph第一行所以不会修改原数组
        auto& p1_row = renderArea->dj_graph[p1_index];
        auto& p2_row = renderArea->dj_graph[p2_index];
        qDebug() << "Before erase, p1_row size: " << p1_row.size();
        p1_row.erase(std::remove_if(p1_row.begin(), p1_row.end(), [p2_index](const Edge& e) {
                         return e.to == p2_index;
                     }), p1_row.end());
        qDebug() << "After erase, p1_row size: " << p1_row.size();

        qDebug() << "Before erase, p2_row size: " << p2_row.size();
        p2_row.erase(std::remove_if(p2_row.begin(), p2_row.end(), [p1_index](const Edge& e) {
                         return e.to == p1_index;
                     }), p2_row.end());
        qDebug() << "After erase, p2_row size: " << p2_row.size();

        renderArea->line_list.erase(renderArea->line_list.begin()+line_index);
        qDebug()<<"line_list.size:"<<renderArea->line_list.size();
    });
    contextMenu.addAction(&action1);
    QAction action2("选项 2", renderArea);
    QAction::connect(&action2, &QAction::triggered, renderArea, [](){
        qDebug() << "选择了选项 2";
    });
    contextMenu.addAction(&action2);
    QPoint globalPos = renderArea->mapToGlobal(pos);
    contextMenu.exec(globalPos);
}
void Utils::buildNormalMenu(RenderArea* renderArea,const QPoint& pos){
    QMenu contextMenu(renderArea);
    QAction action1("添加点", renderArea);
    QAction::connect(&action1, &QAction::triggered, renderArea, [&pos,renderArea](){
        int x = pos.x();
        int y = pos.y();
        Utils::addPoint(renderArea,x,y);
    });
    contextMenu.addAction(&action1);
    QPoint globalPos = renderArea->mapToGlobal(pos);
    contextMenu.exec(globalPos);
}

int Utils::getIdxOfRandomPointInRect(const RenderArea& renderArea,const QPoint& pos){
    for(int i=0;i<renderArea.point_list.size();i++){
        // 这里有个问题就是随机点间隔必须稍微大一点，不然就会出现点击一个位置有多个点响应的情况
        QPoint leftTop(pos.x()-5,pos.y()-5);
        if(QRect(leftTop,QSize(10,10)).contains(renderArea.point_list[i])){
            qDebug()<<"点击区域内存在随机点随机点为"<<renderArea.point_list[i];
            return i;
        }
    }
    return -1;
}
int Utils::getIdxOfLine(const RenderArea& renderArea,const QPoint& pos){
    for(int x=0;x<renderArea.line_list.size();x++){
        if(Utils::isPointOnLine(QLineF(renderArea.line_list[x]),QPointF(pos))){
            qDebug()<<"点击了线"<<renderArea.line_list[x];
            return x;
        }
    }
    return -1;
}

// 迪杰斯特拉实现
// Dijkstra算法实现
std::pair<std::vector<int>, std::vector<int>> dijkstra(const std::vector<std::vector<Edge>>& graph, int start) {
    int n = graph.size();
    std::vector<int> dist(n, std::numeric_limits<int>::max());  // 起点到各点的距离
    std::vector<int> prev(n, -1);  // 前驱节点，用于重建路径
    std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int>>, std::greater<>> pq;  // 优先队列

    dist[start] = 0;
    pq.emplace(0, start);

    while (!pq.empty()) {
        auto [d, u] = pq.top();
        pq.pop();

        if (d > dist[u]) continue;  // 跳过旧数据

        for (const auto& edge : graph[u]) {
            int v = edge.to;
            int w = edge.weight;
            if (dist[u] + w < dist[v]) {
                dist[v] = dist[u] + w;
                prev[v] = u;  // 更新前驱节点
                pq.emplace(dist[v], v);
            }
        }
    }
    return {dist, prev};
}
// 构建dj_graph，这个函数似乎可以使用多次，每次添加点或者删除点以后需要重新生成dj_graph，
// 但是重新生成似乎效率过低，如果可以删除指定点时去除指定的元素就可以了，似乎也可以使用那个调换到最后的函数

// 目前被废弃了，因为加入点时我就给这个点的一行开辟了新的一行，删除点的时候也删除了dj_graph里面的
// 那点的一行以及其它所有to到它的edge。所以目前无需额外build就可以在正常建立点的过程中制造出dj_graph
void Utils::buildDjGraph(RenderArea* renderArea){
    // 用之前的无向图在这里构建新的graph的第一个to，用并通过查找对应的point计算权重并存储

    // 遍历无向图对每一行进行遍历比如第一行查找到几是1，如果是就在dj_graph的第一行加一个Edge结构,同时计算两个节点之间的距离作为weight
    // 而且这里遍历用最大值会导致越界如果此时的没那么大
    for(int row_index=0;row_index<renderArea->maxPointNum;row_index++){
        for(int col_index=0;col_index<renderArea->maxPointNum;col_index++){
            if(renderArea->undirected_graph[row_index][col_index]==1){

            }
        }
    }
}

// 重建路径
std::vector<int> Utils::buildPath(const std::vector<int>& prev, int end) {
    std::vector<int> path;
    for (int u = end; u != -1; u = prev[u]) {
        path.push_back(u);
    }
    reverse(path.begin(), path.end());  // 反转路径，使其从起点到终点
    return path;
}

// 计算两个点的欧式距离，结果取整数
int Utils::euclideanDistanceSquared(QPoint p1,QPoint p2) {
    int x1 = p1.x();
    int x2 = p1.y();
    int y1 = p2.x();
    int y2 = p2.y();
    int dx = x1 - x2;
    int dy = y1 - y2;
    return static_cast<int>(std::sqrt(dx * dx + dy * dy));
}
